module ecs {
    export class Transform {
        public readonly Entity: Entity;

        private hierarchyDirty: DirtyType;

        private _localDirty: boolean;
        private _localPositionDirty: boolean;
        private _localScaleDirty: boolean;
        private _localRotationDirty: boolean;
        private _positionDirty: boolean;
        private _worldToLocalDirty: boolean;
        private _worldInverseDirty: boolean;

        private _localPosition: engine.Vector2;
        private _localScale: engine.Vector2;
        private _localRotation: number;

        private _position: engine.Vector2;
        private _scale: engine.Vector2;
        private _rotation: number;

        private _localTransform: Matrix2D;

        private _rotationMatrix: Matrix2D;
        private _translationMatrix: Matrix2D;
        private _scaleMatrix: Matrix2D;

        private _worldTransform: Matrix2D = Matrix2D.Identity;
        private _worldToLocalTransform: Matrix2D = Matrix2D.Identity;
        private _worldInverseTransform: Matrix2D = Matrix2D.Identity;

        /** 此Transform的子级总数 */
        public get ChildCount(): number{
            return this._children.length;
        }

        private _parent: Transform;
        public set Parent(value: Transform){
            this.SetParent(value);
        }
        public get Parent(): Transform{
            return this._parent;
        }

        private _children: Array<Transform> = [];

        constructor(entity: Entity){
            this.Entity = entity;
        }

        /** 返回索引处的变换子级 */
        public GetChild(index: number): Transform{
            return this._children[index];
        }

        /**
         * 设置Transform的父Transform
         * @param parent 
         */
        public SetParent(parent: Transform){
            if (this._parent == parent)
                return this;

            const parentIndex = this._parent._children.indexOf(this._parent);
            if (parentIndex != -1){
                this._parent._children.splice(parentIndex);
            }

            if (parent){
                parent._children.push(this);
            }

            this._parent = parent;
            this.SetDirty(DirtyType.PositionDirty);

            return this;
        }

        private SetDirty(dirtyFlagType: DirtyType){
            if ((this.hierarchyDirty & dirtyFlagType) == 0){
                this.hierarchyDirty |= dirtyFlagType;

                switch (dirtyFlagType){
                    case DirtyType.PositionDirty:
                        this.Entity.OnTransformChanged(transform.Component.Position);
                        break;
                    case DirtyType.RotationDirty:
                        this.Entity.OnTransformChanged(transform.Component.Rotation);
                        break;
                    case DirtyType.ScaleDirty:
                        this.Entity.OnTransformChanged(transform.Component.Scale);
                        break;
                }

                for (let i = 0; i < this._children.length; i++) {
                    this._children[i].SetDirty(dirtyFlagType);
                }
            }
        }

        private UpdateTransform(){
            if (this.hierarchyDirty != DirtyType.Clean){
                if (this.Parent != null)
                    this.Parent.UpdateTransform();

                if (this._localDirty){
                    if (this._localPositionDirty){
                        this._translationMatrix = Matrix2D.CreateTranslation(this._localPosition.X, this._localPosition.Y, this._translationMatrix);
                        this._localPositionDirty = false;
                    }

                    if (this._localRotationDirty){
                        this._rotationMatrix = Matrix2D.CreateRotation(this._localRotation, this._rotationMatrix);
                        this._localRotationDirty = false;
                    }

                    if (this._localScaleDirty){
                        this._scaleMatrix = Matrix2D.CreateScale(this._localScale.X, this._localScale.Y, this._scaleMatrix);
                        this._localScaleDirty = false;
                    }

                    const t1 = Matrix2D.Multiply(this._scaleMatrix, this._rotationMatrix, this._localTransform);
                    this._scaleMatrix = t1[0];
                    this._rotationMatrix = t1[1];
                    this._localTransform = t1[2];

                    const t2 = Matrix2D.Multiply(this._localTransform, this._translationMatrix, this._localTransform);
                    this._localTransform = t2[0];
                    this._translationMatrix = t2[1];
                    this._localTransform = t2[2];

                    if (!this.Parent){
                        this._worldTransform = this._localTransform;
                        this._rotation = this._localRotation;
                        this._scale = this._localScale;
                        this._worldInverseDirty = true;
                    }


                    this._localDirty = false;
                }

                if (this.Parent){
                    const t3 = Matrix2D.Multiply(this._localTransform, this.Parent._worldTransform, this._worldTransform);
                    this._localTransform = t3[0];
                    this.Parent._worldTransform = t3[1];
                    this._worldTransform = t3[2];

                    this._rotation = this._localRotation + this.Parent._rotation;
                    this._scale = engine.Vector2.Multiply(this.Parent._scale, this._localScale);
                    this._worldInverseDirty = true;
                }

                this._worldToLocalDirty = true;
                this._positionDirty = true;
                this.hierarchyDirty = DirtyType.Clean;
            }
        }
    }

    export enum DirtyType{
        Clean,
        PositionDirty,
        ScaleDirty,
        RotationDirty,
    }
}

module transform{
    export enum Component{
        Position,
        Scale,
        Rotation
    }
}
